import { JXDef } from '../../../conventions/JXCommon';
import { JXActionMgr } from '../../../Core/FrameEx/ActionEx';
import { MapWrap } from '../../../Core/FrameEx/ES5Ex';
import GParam from '../../../Core/GEvent/GParam';
import { CMsg, GRID_TYPE, INFO_TYPE } from '../../Common/Define';
import { Res } from '../../Common/UIResources';
import GameMgr from '../../Logic/GameMgr';
import { FiniteStateMachine, FSMState } from './../../../Core/BaseFSM/FiniteStateMachine';
import { INVALID_VALUE } from './../../../Core/CoreDefine';
import { QUEUE_ACTION_TAG } from './../../../Core/FrameEx/ActionEx';
import { CCNodeImpl } from './../../../Core/FrameEx/CCNodeImpl';
import { GCtrl } from './../../../Core/GCtrl';
import { JXColor } from './../../Common/JXColor';
import { JXBattleCb } from './JXBattleCb';
import { JXBattleEvt, JXBtlAns_PressModel, JXBtlArrIx, JXBtlBEMT, JXBtlCamp } from './JXBattleDefine';
import JXBtlMgr from './JXBattleManager';
import { JXRBBuff } from './JXRBBuff';
import JXRBCmdMgr, { ANIMATION_SPEED } from './JXRBCmdMgr';
import { JXRBCollisionEffect } from './JXRBCollisionEffect';
import JXRBRoleHead from './JXRBRoleHead';
import { JXRBScript } from './JXRBScript';
import { COMMON_ANI_NAME, DescribeGroup, DescribeType, DESCRIPT_SCALE, ICampKeyCode, JXBattleLog, JXEAniEventName, JXEAniNames, JXEDir, JXEState, JXMsg, JXRB_LAYER_INDEX, JXRB_ROLE_LAYER_NAME, JXSceneEffect, SCENE_EFFECT_NODE_NAME } from './JXULDefine';
const DESCRIBE_DURATION_CONDIGS = [0.1, 0.4]
//角色状态机
export class JXRoleSt extends FSMState<JXRBRole, JXEState> {
	protected get sk(): sp.Skeleton {
		return this.entity.skeleton;
	}
	constructor() {
		super();

	}
}

/**待机状态 */
export class JXIDleSt extends JXRoleSt {
	public get stateID(): JXEState {
		return JXEState.Idle;
	}

	public enter() {
		this.entity.changeAnimation(JXEAniNames.idle, true);
	}

}

/**奔跑状态 */
export class JXRunSt extends JXRoleSt {
	public get stateID(): JXEState {
		return JXEState.Run;
	}

	public enter() {
		this.entity.changeAnimation(this.entity.postfix ? JXEAniNames.run2 : JXEAniNames.run, true);
		this.entity.smokeSkeleton.node.parent.active = true;
		this.entity.smokeSkeleton.setAnimation(0, "animation", true)
	}

	public exit(): void {
		this.entity.smokeSkeleton.node.parent.active = false;
	}

	public execute(dt: number) {
		let entity = this.entity;
		if (!entity.airPos) {
			return;
		}
		if (entity.airPos.x > entity.position.x) {
			entity.setDir(JXEDir.Right);
			entity.position = entity.position.add(entity.vSpeedX.mul(dt));
			entity.curPos = entity.position;
			if (entity.position.x > entity.airPos.x) {
				this.goToNextPos();
			}
		} else if (entity.airPos.x < entity.position.x) {
			entity.setDir(JXEDir.Left);
			entity.position = entity.position.add(entity.vSpeedX.mul(dt * -1));
			entity.curPos = entity.position;
			if (entity.position.x < entity.airPos.x) {
				this.goToNextPos();
			}
		} else {
			let pos = entity.cmd.btlMapConverts.converToMapPos(entity.position);
			if (pos.x > 3) {
				entity.setDir(JXEDir.Left);
			} else {
				entity.setDir(JXEDir.Right);
			}
			if (entity.airPos.y > entity.position.y) {
				entity.position = entity.position.add(entity.vSpeedY.mul(dt));
				entity.curPos = entity.position;
				if (entity.position.y > entity.airPos.y) {
					this.goToNextPos();
				}
			} else {
				entity.position = entity.position.add(entity.vSpeedY.mul(dt * -1));
				entity.curPos = entity.position;
				if (entity.position.y < entity.airPos.y) {
					this.goToNextPos();
				}
			}
		}
	}

	protected goToNextPos() {
		this.entity.position = this.entity.airPos;
		if (this.entity.roadIndex < this.entity.road.length - 1) {
			this.entity.roadIndex++;
			this.entity.setAirPos(this.entity.road[this.entity.roadIndex])
		} else {
			let isBoss = this.entity.unitRaw.type === 1;
			let domage: number = isBoss ? GameMgr.systemConfig.value<number>(JXDef.SYS_CONFIG_KEY.bossNpc) : GameMgr.systemConfig.value<number>(JXDef.SYS_CONFIG_KEY.weakNpc)
			let ansBarcksInfo: JXAnsBarracksHurtInfo = {
				insId: null,
				execer: this.entity.id,
				damage: domage,
			}
			JXBtlMgr.ins.onEvent(JXBattleEvt.HURT_BARKS, this.entity.id, ansBarcksInfo);
			this.entity.onEndDie();
		}
	}
}

/**普攻状态机 */
export class JXComAtkSt extends JXRoleSt {
	public get stateID(): JXEState {
		return JXEState.ComAtk;
	}

	public enter() {
		this.entity.changeAnimation((JXEAniNames.comAtk), false);
		this.entity.skeleton.setCompleteListener(this.onComAtkAnimationEnd.bind(this))
	}

	public onComAtkAnimationEnd() {
		this.entity.changePreState();
	}
}

/**技能状态机 */
export class JXSkillSt extends JXRoleSt {
	public get stateID(): JXEState {
		return JXEState.Skill;
	}

	public exit(): void {
		// this.onSkillAnimationEnd();
	}

	public onSkillAnimationEnd() {
		this.entity.changePreState();
	}

}
/**预备状态机 */
export class JXPrepareSt extends JXRoleSt {
	public perpareAniName: string;
	public skillId: number;
	public get stateID(): JXEState {
		return JXEState.Perpare;
	}

	public enter() {
		this.entity.changeAnimation(this.perpareAniName, true);
	}
}

/**死亡状态 */
export class JXDeathSt extends JXRoleSt {
	public get stateID(): JXEState {
		return JXEState.Death;
	}

	public enter() {
		this.entity.addAction(cc.sequence(cc.delayTime(0.5), cc.callFunc(() => {
			this.entity.dieSkeleton.node.active = true;
			this.entity.dieSkeleton.setAnimation(0, "siwang", false)
			this.entity.changeAnimation(JXEAniNames.die, false);
		}), cc.delayTime(0.5), cc.fadeOut(0.5), cc.callFunc(() => {
			this.onAnimationEnd();
		})));
	}
	public onAnimationEnd() {
		this.entity.onEndDie();
	}
}


/**受击状态机 */
export class JXHitSt extends JXRoleSt {
	/**伤害信息队列 */
	protected _damageList: JXAnsHurtInfo[] = [];
	protected duration: number;

	public get stateID(): JXEState {
		return JXEState.Hit;
	}

	public addDamage(hurtInfo: JXAnsHurtInfo) {
		this._damageList.push(hurtInfo);
	}

	public clearDamageList() {
		this._damageList = [];
	}

	public enter() {
		if (!this.duration) {
			let animation = this.entity.skeleton.getAnimationInfo(JXEAniNames.hit)
			this.duration = animation ? animation.duration : 0;
		}
		if (this._damageList.length == 0) {
			this.entity.changePreState();
			return;
		}
		if (!this.isInAnimation()) {
			let hurtInfo = this.showDamage();
			this.entity.showHitAni();
			this.entity.changeAnimation(JXEAniNames.hit, false, () => {
				if (this._damageList.length > 0) {
					this.enter();
					return;
				}
				if (hurtInfo.targetIsDie) {
					this.entity.changeState(JXEState.Death);
					return;
				}
				if (this.entity.camp === JXBtlCamp.Attacker) {
					this.entity.changeState(JXEState.Run);
				} else {
					this.entity.changeState(JXEState.Idle);
				}
			})
		}
	}

	public execute() {
		if (this._damageList.length > 0) {
			let hurtInfo = this.showDamage();
			if (hurtInfo.targetIsDie) {
				this.entity.changeState(JXEState.Death);
				this.clearDamageList();
				return;
			}
		}
	}
	public showDamage() {
		let val = this._damageList.shift();
		this.entity.stateDamage(val);
		return val;
	}

	protected isInAnimation(): boolean {
		let curTrack = this.entity.skeleton.getCurrent(0)
		if (curTrack.animation.name == JXEAniNames.hit) {
			return curTrack.trackTime < curTrack.animationEnd;
		}
		return false;
	}
}

/**停止 */
export class JXStopFrameSt extends JXRoleSt {
	public buffInsIds: number[] = [];

	public get stateID(): JXEState {
		return JXEState.StopFrame;
	}

	public enter() {
		let sp = this.entity.skeleton;
		sp.paused = true;
	}

	public execute() {
		if (this.buffInsIds.length == 0) {
			let sp = this.entity.skeleton;
			sp.paused = false;
			this.entity.changeState(JXEState.Idle);
		}
	}

	public exit() {
		let sp = this.entity.skeleton;
		sp.paused = false;
		this.buffInsIds = []
	}
}

/**恢复状态 */
export class JXRecoverSt extends JXRoleSt {
	protected _addList: JXAnsAddHpInfo[] = [];
	protected duration: number = 0.2;

	public get stateID(): JXEState {
		return JXEState.Real;
	}

	public addHp(ansAddInfo: JXAnsAddHpInfo) {
		this._addList.push(ansAddInfo);
	}

	public enter() {
		this._curTime = 0;
	}

	public execute(dt: number) {
		this._curTime += dt;
		if (this._addList.length == 0) {
			this.entity.changePreState();
			return;
		}
		else {
			if (this._curTime > this.duration) {
				this._curTime = 0;
				let ansInfo = this._addList.shift();
				this.entity.stateAddHp(ansInfo);
			}
		}
	}
}

/**碰撞状态 */
export class JXCollision1St extends JXRoleSt {
	public get stateID(): JXEState {
		return JXEState.Collision1;
	}

	public enter() {
		this.entity.changeAnimation(JXEAniNames.hit, false, () => {
			this.entity.changePreState();
		})
	}

}
/**眩晕状态 */
export class JXVertigoSt extends JXRoleSt {
	public buffInsIds: number[] = [];

	public get stateID(): JXEState {
		return JXEState.Vertigo;
	}

	public enter() {
		this.entity.changeAnimation(JXEAniNames.vertigo, true);
	}

	public execute() {
		if (this.buffInsIds.length == 0) {
			this.entity.changeState(JXEState.Idle);
		}
	}
}

export class JXGSt extends JXRoleSt {
	public waitShowBuffs: string[] = [];
	protected duration: number = 0.2;
	protected _oldModel: string = null;
	protected _curModel: string = null;
	protected _waitChageModel = false;
	public get stateID(): JXEState {
		return JXEState.Global;
	}

	public enter() {
		this._curTime = 0;
	}

	public execute() {
		if (this.waitShowBuffs.length > 0) {
			this._curTime += cc.director.getDeltaTime();
			if (this._curTime > this.duration) {
				this._curTime = 0;
				let uuid = this.waitShowBuffs.shift();
				this.entity.showUIBuff(uuid);
			}
		};
		if (this.entity._script == null && this._waitChageModel) {
			this.changeModel();
		}
	}

	public setModel(model: string) {
		if (this._curModel == model) return;
		if (this._curModel) {
			this._oldModel = this._curModel;
		}
		this._curModel = model;
		if (this._oldModel) {
			this._waitChageModel = true;
		}
	}

	public reOldModel() {
		if (!this._oldModel) return;
		this.setModel(this._oldModel);
	}

	public changeModel() {
		if (this.entity.getCurState() == JXEState.Hit) {
			return;
		}
		if (this.entity.getCurState() == JXEState.Death) {
			this._waitChageModel = false;
			return;
		}
		this._waitChageModel = false;
		this.entity.skeleton.skeletonData = this.entity.cmd.assetManager.assetImpl.getPreLoadAsset<sp.SkeletonData>(this._curModel)
		this.entity.changeState(JXEState.Idle, true);
		if (this.entity.head) {
			this.entity.head.node.y = this.entity.skeleton.node["sHeight"];
		}
	}

}

export class JXRBRole extends CCNodeImpl implements IComLike {
	public _name: string
	/** 位置信息 */
	public iKey: number;
	/** 角色ID */
	public id: string;

	/** 骨骼动画 */
	public skeleton: sp.Skeleton;
	public effectSkeleton: sp.Skeleton;
	public hitSkeleton: sp.Skeleton;
	public dieSkeleton: sp.Skeleton;
	public fogSkeleton: sp.Skeleton;
	public smokeSkeleton: sp.Skeleton;
	public lvUpSkeleton: sp.Skeleton;
	public cmd: JXRBCmdMgr;
	public dir: JXEDir = JXEDir.Right
	public camp: JXBtlCamp = JXBtlCamp.Attacker;
	public road: cc.Vec2[] = []
	public airPos: cc.Vec2 = null;
	public roadIndex: number = 1;
	/**在战斗管理中的索引 */
	public index: number = INVALID_VALUE;
	public postfix: boolean = false;
	/** 静态表英雄表ID */
	public tableId: number = INVALID_VALUE;
	public vSpeedX: cc.Vec2 = cc.Vec2.ZERO;
	public vSpeedY: cc.Vec2 = cc.Vec2.ZERO;
	public wheatUp: number = 0;
	private _unitId: number = INVALID_VALUE;
	public unitRaw: SUnitDataRaw;
	public set unitId(v: number) {
		if (v) {
			this.unitRaw = JXBattleCb.getUnitData(v);
			this.skeleton.node.scale = this.unitRaw.scale;
			this.fogSkeleton.node.scale *= this.unitRaw.scale;
			this.fogSkeleton.node.active = !!this.unitRaw.fog;
			if (this.unitRaw.fog === 2) {
				this.fogSkeleton.setAnimation(0, "zise", true)
			} else {
				this.fogSkeleton.setAnimation(0, "zise2", true)
			}
			this._unitId = v;
		} else {
			this.skeleton.node.scale = GameMgr.systemConfig.value<number>(JXDef.SYS_CONFIG_KEY.defScale);
		}
	}
	private _birthPos: cc.Vec2 = cc.Vec2.ZERO;
	/**当前的等级 */
	private _lv: number = 1;
	public get lv(): number {
		return this._lv;
	}

	public set lv(v: number) {
		this._lv = v;
		if (this.head) {
			this.head.setLv(v);
		}
	}

	public get birthPos() {
		if (this.camp === JXBtlCamp.Defenser) {
			return cc.v2(this._birthPos.x, this._birthPos.y)
		} else {
			return cc.v2(this._curPos.x, this._curPos.y);
		}
	}
	private _curPos: cc.Vec2 = cc.Vec2.ZERO;
	public get curPos() {
		return cc.v2(this._curPos.x, this._curPos.y)
	}

	public set curPos(v: cc.Vec2) {
		this._curPos = cc.v2(v.x, v.y);
	}
	/**初始化头部血条 */
	protected _head: JXRBRoleHead;
	public get head(): JXRBRoleHead {
		return this._head;
	}

	/** 战斗动作管理器 */
	public btlActionMgr: JXActionMgr;

	/**飘字*/
	protected _describeList: MapWrap<number, { type: DescribeType, text: string }[]>;
	protected _describeDeltas: number[] = [0, 0, 0, 0, 0, 0];
	/**阴影-影子*/
	protected _shadow: cc.Node
	//普攻ID
	public comAtkActionId = null
	// Space Logic DATA
	protected _curHp: number;
	public get curHp() {
		return this._curHp;
	}
	/**护盾血量 */
	protected _tempHp: number = 0;
	public get tempHp() {
		return this._tempHp;
	}

	/**最大血量 */
	protected _maxHp: number;
	public initHp(maxHp: number) {
		this._maxHp = maxHp;
		this._curHp = this._maxHp;
		if (this._head) {
			this._head.setHp(this._maxHp, this._curHp);
		}
	}

	public updateHp(cur: number, max: number) {
		this._maxHp = max;
		this._curHp = cur;
		if (this._head) {
			this._head.setHp(this._maxHp, this._curHp);
		}
	}

	/** buffs */
	public _skillBuffs: MapWrap<number, MapWrap<number, JXRBBuff>> = new MapWrap<number, MapWrap<number, JXRBBuff>>();
	public get skillBuffs() {
		return this._skillBuffs;
	}


	private _waveTime: number = 0;
	/** 当前执行的脚本 */
	public _script: JXRBScript;
	public curScript: JXRBScript = null;

	/**状态机 */
	public fsm: FiniteStateMachine<JXRBRole, JXEState>;
	protected _runSt: JXRunSt;
	protected _idleSt: JXIDleSt;
	protected _comAtkSt: JXComAtkSt;
	protected _deathSt: JXDeathSt;
	protected _skillSt: JXSkillSt;
	protected _prepareSt: JXPrepareSt;
	protected _hitSt: JXHitSt;
	protected _stopFrameSt: JXStopFrameSt;
	protected _globalSt: JXGSt;
	protected _recoverSt: JXRecoverSt;
	protected _vertigoSt: JXVertigoSt;
	protected _collision1St: JXCollision1St;

	protected _touchMove: boolean = false;
	protected _touchBegin: boolean = false;
	protected _isDraging: boolean = false;
	private _canDraw: boolean = false;
	/** 等待这些角色执行完成对应的事件，才进行后续 */
	protected _waitRoles: JXRBRole[] = [];
	protected _running = true;
	public set running(active) {
		this._running = active;
	}

	public pushWaitRoles(JXRBRole: JXRBRole) {
		this._waitRoles.push(JXRBRole)
	}

	/** 被动命令 */
	protected _passiveCmds = null;

	public addPassiveCmds(passiveCmd) {
		this._passiveCmds = passiveCmd;
	}

	public isPassiveCmd(): boolean {
		return !!this._passiveCmds;
	}

	/** 动作队列 */
	protected _actionList: cc.Action[];
	/**初始化 */
	constructor(object: cc.Node) {
		super(object);
		this.btlActionMgr = new JXActionMgr();
		this._actionList = [];
		this._describeList = new MapWrap<number, { type: DescribeType, text: string }[]>();
		this._describeDeltas = [0, 0, 0, 0, 0, 0];
		this._script = null;
		this.skeleton = object.getChildComByName('skeleton', sp.Skeleton);
		if (!this.skeleton) {
			// throw new Error('the JXClientRole instantiate failed! because of the role targetNode missing component: sp.Skeleton!');
			// 如果没有默认的骨骼组件，则表示这是一个环境对象
			return;
		}
		this.fogSkeleton = object.getChildComByName("yanwu", sp.Skeleton);
		this.hitSkeleton = this.skeleton.node.getChildComByName("hit", sp.Skeleton);
		this.dieSkeleton = this.skeleton.node.getChildComByName("die", sp.Skeleton);
		this.smokeSkeleton = object.getChildByName("smoke").getChildComByName("smoke", sp.Skeleton);
		this.lvUpSkeleton = object.getChildByName("lvUp").getComponent(sp.Skeleton);
		this.skeleton.setEventListener((track: sp.spine.TrackEntry, event: sp.spine.Event) => {
			this.onSkEventListen(this.skeleton, track, event);
		});
		this._shadow = this.target.getChildByName('shadow');
		// 创建技能特效层
		let effectNode = new cc.Node('skilleffectNode');
		let effectSkeleton = effectNode.addComponent(sp.Skeleton);
		effectSkeleton.premultipliedAlpha = false;
		this.target.addChild(effectNode);
		effectNode.position = cc.Vec2.ZERO; //cc.v2(50, 0);
		effectSkeleton.skeletonData = null;
		this.effectSkeleton = effectSkeleton;
		JXBattleLog(this.wrapId, " role init");
		this.start();
	}


	public changePreState() {
		let preState = this.getPreState();
		if (preState === JXEState.Idle && this.camp === JXBtlCamp.Attacker) {
			this.changeState(JXEState.Run);
		} else {
			this.changeState(this.getPreState());
		}
	}

	/**设置 */
	public setEffectSkeletonData(data: sp.SkeletonData) {
		if (this.effectSkeleton) {
			this.effectSkeleton.skeletonData = data;
			this.effectSkeleton.timeScale = ANIMATION_SPEED * this.cmd.getSpeed();
			this.effectSkeleton.setEventListener((track, event) => {
				this.onSkEventListen(this.effectSkeleton, track, event);
			});
		}
	}

	/**设置模型 */
	public setModel(model: string) {
		this._globalSt.setModel(model);
	}

	/**设置出生点 */
	public setBirthPos(birthPos: cc.Vec2) {
		this.position = this.cmd.btlMapConverts.converToCCPos(birthPos);
		this.curPos = this.position;
		this._birthPos = this.cmd.btlMapConverts.converToCCPos(birthPos);
	}

	public setAirPos(airPos: cc.Vec2) {
		this.airPos = this.cmd.btlMapConverts.converToCCPos(airPos);
	}

	/**设置阵营 */
	public setCamp(camp: JXBtlCamp) {
		this.camp = camp;
	}


	/**设置路径 */
	public setRoad(road: number[]) {
		this.road = [];
		road.forEach(pos => {
			this.road.push(GameMgr.mapMgr.decodeXxyy(pos))
		})

	}

	/** 方向 */
	public setDir(dir: JXEDir) {
		this.dir = dir;
		this.smokeSkeleton.node.parent.scaleX = Math.abs(this.smokeSkeleton.node.parent.scaleX) * this.dir;
		this.skeleton.node.scaleX = Math.abs(this.skeleton.node.scaleX) * this.dir;
		this.effectSkeleton.node.scaleX = this.dir;
	}

	public initHeadCtrl(head: JXRBRoleHead) {
		this._head = head;
		if (CC_DEV) {
			let color: cc.Color, colorOutLine: cc.Color;
			if (this.iKey < ICampKeyCode) {
				color = JXColor.CA4FF9F;
			} else {
				color = JXColor.CFFF075;
			}
			if (CC_DEV) {
				this._head.setName(this.id + this._name, color);

			} else {
				this._head.setName(this._name, color);
			}
		}
		head.node.y = this.skeleton.node["sHeight"] * this.skeleton.node.scale;
		if (this.unitRaw) {
			head.node.active = this.unitRaw.type === 1;
		}
		this._head.setHpBarColor(this.camp);
	}

	/********************************************************************************状态机***************************************************************************************** */

	/**初始化状态机 */
	public initState() {
		this.fsm = new FiniteStateMachine<JXRBRole, JXEState>(this);
		this._runSt = new JXRunSt();
		this._idleSt = new JXIDleSt();
		this._comAtkSt = new JXComAtkSt();
		this._deathSt = new JXDeathSt();
		this._skillSt = new JXSkillSt();
		this._prepareSt = new JXPrepareSt();
		this._hitSt = new JXHitSt();
		this._recoverSt = new JXRecoverSt();
		this._collision1St = new JXCollision1St();
		this._vertigoSt = new JXVertigoSt();
		this._stopFrameSt = new JXStopFrameSt();

		this.fsm.registerState(this._stopFrameSt);
		this.fsm.registerState(this._runSt);
		this.fsm.registerState(this._idleSt);
		this.fsm.registerState(this._comAtkSt);
		this.fsm.registerState(this._deathSt);
		this.fsm.registerState(this._skillSt);
		this.fsm.registerState(this._prepareSt);
		this.fsm.registerState(this._hitSt);
		this.fsm.registerState(this._vertigoSt);
		this.fsm.registerState(this._recoverSt);
		this.fsm.registerState(this._collision1St);

		this._globalSt = new JXGSt();
		this.fsm.registerState(this._globalSt);
		this.fsm.setGlobalState(this._globalSt);
	}

	/**改变状态 */
	public changeState(state: JXEState, force: boolean = false) {
		/**死亡不表现受击 */
		let curState = this.getCurState();
		if (curState == this._deathSt.stateID && state == this._hitSt.stateID) {
			return;
		}
		this.fsm.changeState(state, force)
	}

	/** 获取当前状态 */
	public getCurState() {
		return this.fsm.getCurState();
	}

	/** 获取之前状态 */
	public getPreState() {
		let prestate = this.fsm.getPerState();
		// 特殊处理一下，因为脚本状态的原因，技能状态不能作为直接返回使用
		if (prestate == this._skillSt.stateID) {
			prestate = this._idleSt.stateID;
		}
		return prestate;
	}

	/********************************************************************************生命周期************************************************************************************/
	public start() {
		this.initEvent();
	}

	public initEvent() {
		this.target.on(cc.Node.EventType.TOUCH_START, this._onTouchStar, this);
		this.target.on(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
		this.target.on(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this);
		this.target.on(cc.Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);

	}

	public update(dt: number) {
		this.btlActionMgr.update(dt);
		this.doSescribe(dt);
		this.doWaveDescribe(dt)
		let curState = this.getCurState();
		if (this.fsm) {
			this.fsm.FSMUpdate(dt);
			if (curState == this._hitSt.stateID || curState == this._recoverSt.stateID || curState == this._collision1St.stateID) return;
		}

		if (!this.btlActionMgr.hasQueueAction(this) && this._actionList.length > 0) {
			this.addActionToMgr(this, this._actionList.shift());
		}

		if (this._waitRoles.length > 0) {
			for (let i = this._waitRoles.length - 1; i >= 0; i--) {
				if (this._waitRoles[i].checkRoleIsEnd()) {
					this._waitRoles.pop();
				}
				else {
					return;
				}
			}
			this._running = true;
		}

		if (this._running) {
			this.doNextCmd();
		}

	}
	/**获取下一条指令 */
	public getNextCmd(): any[] {
		if (this._passiveCmds) {
			let top = this._passiveCmds[JXBtlArrIx.Zero];
			if (top) {
				let cmd = top.shift();
				if (top.length == 0) {
					this._passiveCmds.shift();
					if (this._passiveCmds.length == 0) {
						this._passiveCmds = null;
					}
				}
				return cmd;
			}
			else {
				this._passiveCmds = null;
			}
		}
		return this.cmd.jxBtlAns.DPL_getCurRecord(this.id, true);
	}

	/** 执行下一个命令*/
	public doNextCmd() {
		if (this.cmd.jxBtlAns.Get_DPL_curPlayID(this.id) >= this.cmd.jxBtlAns.DPL_getRecordsByID(this.id).length) {
			return;
		}
		let cmd = this.getNextCmd();
		this.cmd.parseCmd(cmd);
	}


	protected _onTouchStar(event: cc.Event.EventTouch) {
		this._canDraw = this.camp === JXBtlCamp.Defenser;
		if (!this._canDraw) {
			return;
		}
		this._touchMove = false;
		this._touchBegin = true;
		JXBtlMgr.ins.getTeamByCamp(this.camp).outBattle(this.index);
		this.cmd.removeDefRole(this.id);
		event.stopPropagation();
	}

	public _onTouchEnd(event: cc.Event.EventTouch, isCancel = false) {
		let worldPos = event.getLocation();
		let fuc = (activePos) => {
			let isPlace = this.cmd.btlCameraMap.selectGridByPos(activePos).data.gridInfo[INFO_TYPE.GRID] === GRID_TYPE.PLACE
			if (isPlace) {
				this.setBirthPos(activePos)
				JXBtlMgr.ins.getTeamByCamp(this.camp).goBtlById(this.index);
				this.cmd.addRole(this, this.id);
			} else {
				this._onTouchCancel();
			}

		}
		if (this._touchMove) {
			let result = this.parent.getBoundingBoxToWorld().contains(worldPos)
			let localPos: cc.Vec2 = this.parent.convertToNodeSpaceAR(worldPos);
			let activePos = this.cmd.btlMapConverts.converToMapPos(localPos);
			if (activePos.x > this.cmd.btlMapConverts.mapSize.width - 1 || activePos.y > this.cmd.btlMapConverts.mapSize.height - 1 || activePos.x < 0 || activePos.y < 0) {
				this._onTouchCancel()
				return;
			}
			if (result) {
				let role = this.cmd.checkCanPutOn(JXBtlCamp.Defenser, activePos);
				if (role) {
					if (role.tableId != this.tableId) {
						role.setBirthPos(this.cmd.btlMapConverts.converToMapPos(this.birthPos));
						fuc(activePos)
					} else {
						if (role.lv === this.lv) {
							this.roleSynthetic(role)
						} else {
							this._onTouchCancel();
						}
					}
				} else {
					fuc(activePos)
				}
			}
			GCtrl.ES.emit(CMsg.client.fight_ui.hideAllItem);
		} else {
			if (isCancel == false) {
			}
		}
		this._touchBegin = false;
		this._touchMove = false;
		this._isDraging = false;
	}



	protected _onTouchMove(event: cc.Event.EventTouch) {
		if (!this._canDraw) {
			return;
		}
		let deltaMove = event.getLocation().sub(event.getStartLocation());
		if (!this._isDraging) {
			this._isDraging = true;
		}
		if (deltaMove.mag() > 7 && this._isDraging) {
			if (!this._touchMove) {
				this._touchMove = true;
			} else {
				let worldPos = event.getLocation();
				let localPos: cc.Vec2 = this.target.parent.convertToNodeSpaceAR(worldPos);
				this.target.setPosition(localPos);
				let activePos = this.cmd.btlMapConverts.converToMapPos(localPos);
				if (!this._curPos || activePos.x != this._curPos.x || activePos.y != this._curPos.y) {
					let info: SelectGridInfo = {
						heroId: this.tableId,
						pos: activePos
					}
					GCtrl.ES.emit(CMsg.client.fight_ui.selectItem, new GParam(info))
					this._curPos = activePos;
				}
			}
		}
	}


	/**触摸取消 */
	private _onTouchCancel() {
		if (!this._canDraw) {
			return;
		}
		this.target.setPosition(this.birthPos);
		JXBtlMgr.ins.getTeamByCamp(this.camp).goBtlById(this.index);
		this.cmd.addRole(this, this.id);
		this._touchBegin = false;
		this._touchMove = false;
		this._isDraging = false;
	}


	/*****************************************************************************buff逻辑处理****************************************************************************************** */


	public addBuff(buff: JXRBBuff) {
		if (!buff) return;
		let buffs: JXRBBuff[] = [];
		let maxLayerIdx = 0;
		this._skillBuffs.forEach(vs => {
			vs.forEach(v => {
				if (v.visLayer == buff.visLayer) {
					buffs.push(v);
					maxLayerIdx = Math.max(v.layerOrder, maxLayerIdx);
				}
			})
		})

		buff.addToTarget(this);

		let skillbuffs = this._skillBuffs.get(buff.skillId);
		skillbuffs.set(buff.index, buff);
		buff.layerOrder = ++maxLayerIdx;
		buffs.push(buff);
		this.visitBuffs(buffs);
		if (buff.state) {
			switch (buff.state) {
				case JXEState.Vertigo: {
					this._vertigoSt.buffInsIds.push(buff.wrapId);
					break;
				}
				case JXEState.StopFrame: {
					this._stopFrameSt.buffInsIds.push(buff.wrapId);
					break;
				}
				case JXEState.TransSpine: {
					let ext = buff.ext;
					this._globalSt.setModel(Res.model + ext + '/' + ext);
					break;
				}
			}
			if (buff.state < JXEState.Global) {
				this.addAction(cc.callFunc(() => {
					this.changeState(buff.state);
				}))
			}
		}
	}
	public addBuffTip(uuid: string) {
		let global = this.fsm.global as JXGSt;
		global.waitShowBuffs.push(uuid);
	}

	public removeBuff(buff: JXRBBuff) {
		let indexBuffs = this._skillBuffs.get(buff.skillId);
		indexBuffs.delete(buff.index);
		if (indexBuffs.size == 0) {
			this._skillBuffs.delete(buff.skillId);
		}
		let buffs: JXRBBuff[] = [];
		this._skillBuffs.forEach(vs => {
			vs.forEach(v => {
				if (v.visLayer == buff.visLayer) {
					buffs.push(v);
				}
			})
		})
		this.visitBuffs(buffs);
		if (buff.state) {
			switch (buff.state) {
				case JXEState.Vertigo: {
					let index = this._vertigoSt.buffInsIds.indexOf(buff.wrapId);
					if (index != INVALID_VALUE) {
						this._vertigoSt.buffInsIds.splice(index, 1);
					}
					break;
				}
				case JXEState.StopFrame: {
					let index = this._stopFrameSt.buffInsIds.indexOf(buff.wrapId);
					if (index != INVALID_VALUE) {
						this._stopFrameSt.buffInsIds.splice(index, 1);
					}
					break;
				}
				case JXEState.TransSpine: {
					if (buff.ext) {
						this._globalSt.reOldModel();
					}
				}
			}
		}
		buff.destroy();
	}


	public removeUIBuff(effectRaw: SSkillEffectRaw) {
		// this._head.removeBuff(effectRaw);
	}

	public visitBuffs(buffs: JXRBBuff[]) {
		if (!buffs || buffs.length == 0) return;
		buffs.sort((a, b) => {
			if (a.effectFirst != b.effectFirst) {
				return a.effectFirst - b.effectFirst;
			}
			return b.layerOrder - a.layerOrder;
		});
		for (let i = 0; i < buffs.length; i++) {
			buffs[i].active = i == JXBtlArrIx.Zero;
		}
	}


	/****************************************************************************战斗状态/表现************************************************************************************ */
	/**合体*/
	public roleSynthetic(role: JXRBRole) {
		JXBtlMgr.ins.getTeamByCamp(this.camp).outBattle(role.index);
		this.cmd.removeDefRole(role.id);
		let sycInfo = JXBtlMgr.ins.getTeamByCamp(this.camp).sycRole(role.index, this.index);
		if (!sycInfo) {
			this._onTouchCancel()
		} else {
			role.lv += 1;
			role.updateHp(sycInfo.curHp, sycInfo.maxHp);
			role.playSyncAni();
			let raw = GameMgr.heroData.getRaw<SHeroDataRaw>(role.tableId);
			// GameMgr.sdkMgr.umaSetPoint(JXDef.umaPoint.battle, { msg: raw.name + "进行合体" });
			this.onEndDie();
		}
		JXBtlMgr.ins.getTeamByCamp(this.camp).goBtlById(role.index);
		this.cmd.addRole(role, role.id);
	}

	/**受伤监测 */
	public onDamage(hurtVal: JXAnsHurtInfo) {
		// 反伤检测
		if (hurtVal.ins2Hurt) {
			let ansInfo = this.cmd.jxBtlAns.hurtInfos[hurtVal.ins2Hurt];
			let role = this.cmd.getRoleById(ansInfo.target);
			role.onDamage(ansInfo);
		}

		// 吸血检测
		if (hurtVal.ins2AtkHp) {
			let ansInfo = this.cmd.jxBtlAns.addHpInfos[hurtVal.ins2AtkHp];
			let role = this.cmd.getRoleById(ansInfo.target);
			role.onAddHp(ansInfo);
		}
		let curState = this.fsm.getCurState();
		if (curState == JXEState.Death) {
			return;
		}
		if (hurtVal.execer == hurtVal.target || (curState == this._stopFrameSt.stateID && !hurtVal.targetIsDie) || !!this._script) {
			this.stateDamage(hurtVal);
			if (hurtVal.targetIsDie && this._script && this._script.existAcions.length == 0) {
				this._script.existAcions = [cc.callFunc(() => this.changeState(JXEState.Death))];
			}
			return;
		}

		this._hitSt.addDamage(hurtVal);
		if (curState != this._hitSt.stateID) {
			this.addAction(cc.callFunc(() => {
				this.changeState(this._hitSt.stateID);
			}));
		}
	}


	/**回血 */
	public onAddHp(addAnsInfo: JXAnsAddHpInfo) {
		let curState = this.fsm.getCurState();
		if (addAnsInfo.execer == addAnsInfo.target || curState == this._stopFrameSt.stateID || !!this._script) {
			this.stateAddHp(addAnsInfo);
			return;
		}
		this._recoverSt.addHp(addAnsInfo);
		if (curState != this._recoverSt.stateID) {
			this.addAction(cc.callFunc(() => {
				this.changeState(this._recoverSt.stateID);
			}))
		}
	}


	/***/
	public onAddMaxHp(addMaxAnsInfo: JXAnsAddMaxHpInfo) {
		this._curHp += addMaxAnsInfo.addNum;
		this._maxHp += addMaxAnsInfo.addNum;
		this._head.setHp(this._maxHp, this._curHp);
		this.showUIAddMaxHp(addMaxAnsInfo);
	}

	/**状态回血 */
	public stateAddHp(addAnsInfo: JXAnsAddHpInfo) {
		if (!addAnsInfo.addNum) return;
		if (addAnsInfo.isProtect) {
			this._tempHp += addAnsInfo.addNum;
			this._head.setTempHp(this._tempHp, this._maxHp);
			this.showUIAddHp(addAnsInfo);
		} else {
			this._curHp += addAnsInfo.addNum;
			this._head.setHp(this._maxHp, this._curHp);
			this.showUIAddHp(addAnsInfo);
		}
	}

	/**死亡 */
	public onDie() {
		this.effectSkeleton.skeletonData = null;
		this.addAction(cc.callFunc(() => {
			this.changeState(JXEState.Death);
		}))
	}

	/**死完了 */
	public onEndDie() {
		if (this.camp === JXBtlCamp.Attacker) {
			let money = this.unitRaw.money;
			this.cmd.addWheat(money);
		}
		this.active = false;
		this.effectSkeleton.skeletonData = null;
		this._shadow.active = false;
		if (this._head) this._head.node.active = false;
		JXBtlMgr.ins.getTeamByCamp(this.camp).outBattle(this.index);
		this.cmd.removeDefRole(this.id);
		setTimeout(() => {
			this.btlActionMgr.removeAllActions();
			this.target.destroy();
		}, 100)


	}

	/**确定是角色是否结束*/
	public checkRoleIsEnd(): boolean {
		let curState = this.fsm.getCurState();
		if (curState === this._hitSt.stateID) {
			return false;
		}
		if ((curState === this._idleSt.stateID && this.camp === JXBtlCamp.Defenser) || (curState == this._runSt.stateID && this.camp === JXBtlCamp.Attacker) || (curState == this._deathSt.stateID && this.active == false) || curState == this._prepareSt.stateID || curState == this._vertigoSt.stateID || curState == this._stopFrameSt.stateID) {
			return true;
		}
		return false;
	}

	/**设置伤害 */
	public stateDamage(hurtVal: JXAnsHurtInfo) {
		this.showUIDamage(hurtVal);
		if (hurtVal.ins2DefHp) {
			let ansInfo = this.cmd.jxBtlAns.addHpInfos[hurtVal.ins2DefHp];
			let role = this.cmd.getRoleById(ansInfo.target);
			role.stateAddHp(ansInfo);
		}
		if (!!hurtVal.protect) {
			this._tempHp -= hurtVal.protect;
			if (!hurtVal.damage) return;
		}
		if (this._curHp) this._curHp -= hurtVal.damage;
		if (this._curHp < 0) {
			//数据矫正
			this._curHp = JXBtlMgr.ins.getEntityById(this.id).curHp;
		}
		if (this._head) {
			this._head.setHp(this._maxHp, this._curHp);
		}
	}

	/**回血飘字 */
	public showUIAddHp(addAnsInfo: JXAnsAddHpInfo) {
		let val = this.cmd.numberParse("+" + addAnsInfo.addNum, addAnsInfo.isCrit)
		if (addAnsInfo.insBuff) {
			let ansBuff = this.cmd.jxBtlAns.buffInfos[addAnsInfo.insBuff];
			if (ansBuff.lifeRd) {
				let effectRaw = GameMgr.skillEffect.getRaw<SSkillEffectRaw>(ansBuff.buffId);
				val = effectRaw.effectDes + '|' + val;
			}
		}
		if (addAnsInfo.ext) {
			val = addAnsInfo.ext + '|' + val;
		}
		this.showDescribe({ type: DescribeType.Cure, text: val });
	}

	/*增加最大回血飘字*/
	protected showUIAddMaxHp(addMaxAnsInfo: JXAnsAddMaxHpInfo) {
		let val = this.cmd.numberParse("+" + addMaxAnsInfo.addNum, false);
		if (addMaxAnsInfo.insBuff) {
			let ansBuff = this.cmd.jxBtlAns.buffInfos[addMaxAnsInfo.insBuff];
			if (ansBuff) {
				let effectRaw = GameMgr.skillEffect.getRaw<SSkillEffectRaw>(ansBuff.buffId);
				val = effectRaw.effectDes + '|' + val;
			}
		}
		if (addMaxAnsInfo.ext) {
			val = addMaxAnsInfo.ext + '|' + val;
		}
		this.showDescribe({ type: DescribeType.MaxHp, text: val });
	}

	/**显示伤害 */
	public showUIDamage(hurtVal: JXAnsHurtInfo) {
		let val = this.cmd.numberParse('-' + (hurtVal.damage + (hurtVal.protect || 0)), hurtVal.isCrit);
		if (hurtVal.insBuff) {
			let buff = this.cmd.jxBtlAns.buffInfos[hurtVal.insBuff];
			if (buff.lifeRd) {
				let effectRaw = GameMgr.skillEffect.getRaw<SSkillEffectRaw>(buff.buffId);
				val = effectRaw.effectDes + '|' + val;
				let collisionEffect = this.createBuffCollisionEffect(hurtVal.insBuff);
				if (collisionEffect) {
					collisionEffect.addToTarget(this);
				}
				else {
					// this.createEffect(hurtVal.protect ? JXStaticEffects.hit1 : JXStaticEffects.hit);
				}
			}
		}
		if (hurtVal.ext) {
			val = hurtVal.ext + '|' + val;
		}
		if (hurtVal.isCrit) {
			val = "effectDes_gcrit" + '|' + val;
		}
		// if (hurtVal.damage == 0) return;
		this.showDescribe({ type: hurtVal.isCrit ? DescribeType.CritHurt : DescribeType.Hurt, text: val });
	}

	/**buff飘字 */
	public showUIBuff(uuid: string) {
		let ansBuff = this.cmd.jxBtlAns.buffInfos[uuid];
		let effectRaw = GameMgr.skillEffect.getRaw<SSkillEffectRaw>(ansBuff.buffId);
		if (!effectRaw) return;
		switch (effectRaw.effectType) {
			case JXBtlBEMT.AddFp:
			case JXBtlBEMT.AddHp: {
				break;
			}
			case JXBtlBEMT.Buff: {
				this.showDescribe({ type: DescribeType.BuffAdd, text: effectRaw.effectDes });
				break;
			}
			case JXBtlBEMT.DeBuff: {
				this.showDescribe({ type: DescribeType.BuffSub, text: effectRaw.effectDes });
				break;
			}
			default: {
				this.showDescribe({ type: DescribeType.Effect, text: effectRaw.effectDes });
				break;
			}
		}
		// this._head.addBuff(effectRaw);
	}


	/**显示描述 */
	public showDescribe(desc: { type: DescribeType, text: string }) {
		if (!desc.text) return;
		let group = desc.type <= DescribeType.MaxHp ? DescribeGroup.Number : DescribeGroup.Effect;
		let describes = this._describeList.get(group);
		if (!describes) {
			describes = [];
			this._describeList.set(group, describes);
		}
		describes.push(desc);
	}


	public createBuffCollisionEffect(uuid: string): JXRBCollisionEffect {
		let ansBuff = this.cmd.jxBtlAns.buffInfos[uuid];
		if (!ansBuff) return;
		let ansSkill = this.cmd.jxBtlAns.skillInfos[ansBuff.insSkillId];
		if (!ansSkill) return;
		let skillRaw = GameMgr.skillData.getRaw<SSkillDataRaw>(ansSkill.skillId);
		if (!skillRaw.eAnimId) return;
		if (!skillRaw.eAnimId[ansBuff.index]) return;
		let eAniRaw = GameMgr.effectAnim.getRaw<SEffectAnimRaw>(skillRaw.eAnimId[ansBuff.index]);
		if (!eAniRaw) return;
		if (!eAniRaw.collision) return;
		let collisionRaw = GameMgr.actionCollision.getRaw<SActionCollisionRaw>(eAniRaw.collision);
		if (!collisionRaw) return;
		return new JXRBCollisionEffect(collisionRaw, this.cmd);
	}

	/**飘字处理 */
	protected doSescribe(dt: number) {
		this._describeList.forEach((desc, gruop) => {
			if (desc.length == 0) return;
			this._describeDeltas[gruop] = this._describeDeltas[gruop] + dt;
			if (this._describeDeltas[gruop] < DESCRIBE_DURATION_CONDIGS[gruop]) return;
			let describe = desc.shift();
			let desNode = this.cmd.assetManager.createDescribe(describe.text);
			desNode.color = JXColor.C000000;
			desNode.parent = this.cmd.rootNode;
			desNode.zIndex = Math.max(this.zIndex, JXRB_LAYER_INDEX.DESCRIBE_LAYER);
			let zeroHeight = this.skeleton.node["sHeight"];
			let tween: cc.Tween = null;
			switch (describe.type) {
				case DescribeType.Hurt: {
					desNode.position = this.cmd.rootNode.convertToNodeSpaceAR(this.target.convertToWorldSpaceAR(cc.v2(0, zeroHeight - 20)));
					desNode.scale = 1 * DESCRIPT_SCALE;
					desNode.opacity = 128;
					tween = cc.tween(desNode).by(0.1, { scale: 0.5 * DESCRIPT_SCALE, opacity: 128, y: 5 }).by(0.2, { scale: -0.5 * DESCRIPT_SCALE, y: 10 }).parallel(
						cc.tween(desNode).by(0.5, { y: 25 }, { easing: "cubicOut" }),
						cc.tween(desNode).delay(0.1).by(0.4, { opacity: -255 })
					)
					break;
				}
				case DescribeType.CritHurt: {
					desNode.color = JXColor.Cff0000;
					desNode.position = this.cmd.rootNode.convertToNodeSpaceAR(this.target.convertToWorldSpaceAR(cc.v2(0, zeroHeight - 20)));
					desNode.scale = 1 * DESCRIPT_SCALE;
					desNode.opacity = 128;
					tween = cc.tween(desNode).by(0.1, { scale: 0.5 * DESCRIPT_SCALE, opacity: 128, y: 5 }).by(0.2, { scale: -0.5 * DESCRIPT_SCALE, y: 10 }).parallel(
						cc.tween(desNode).by(0.5, { y: 25 }, { easing: "cubicOut" }),
						cc.tween(desNode).delay(0.1).by(0.4, { opacity: -255 })
					);
					break;
				}
				case DescribeType.Cure: {
					desNode.position = this.cmd.rootNode.convertToNodeSpaceAR(this.target.convertToWorldSpaceAR(cc.v2(0, zeroHeight - 20)));
					desNode.scale = 1 * DESCRIPT_SCALE;
					desNode.opacity = 128;
					tween = cc.tween(desNode).by(0.1, { scale: 0.5 * DESCRIPT_SCALE, opacity: 128, y: 5 }).by(0.2, { scale: -0.5 * DESCRIPT_SCALE, y: 10 }).parallel(
						cc.tween(desNode).by(0.5, { y: 25 }, { easing: "cubicOut" }),
						cc.tween(desNode).delay(0.1).by(0.4, { opacity: -255 })
					)
					break;
				}
				case DescribeType.MaxHp: {
					desNode.position = this.cmd.rootNode.convertToNodeSpaceAR(this.target.convertToWorldSpaceAR(cc.v2(0, zeroHeight + 20)));
					desNode.scale = 1 * DESCRIPT_SCALE;
					desNode.opacity = 128;
					tween = cc.tween(desNode).by(0.1, { scale: 0.5 * DESCRIPT_SCALE, opacity: 128, y: 5 }).by(0.2, { scale: -0.5 * DESCRIPT_SCALE, y: 10 }).delay(0.2).parallel(
						cc.tween(desNode).by(0.5, { y: 25 }, { easing: "cubicOut" }),
						cc.tween(desNode).delay(0.1).by(0.4, { opacity: -255 })
					)
					break;
				}
				case DescribeType.BuffAdd: {
					desNode.scale = 1 * DESCRIPT_SCALE;
					desNode.opacity = 128;
					desNode.position = this.cmd.rootNode.convertToNodeSpaceAR(this.target.convertToWorldSpaceAR(cc.v2(0, zeroHeight / 2 - 20)));
					tween = cc.tween(desNode).parallel(
						cc.tween(desNode).to(0.2, { opacity: 255 }).to(0.8, { opacity: 0 }),
						cc.tween(desNode).by(0.7, { y: 25 }, { easing: "cubicOut" })
					)
					break;
				}
				case DescribeType.BuffSub: {
					desNode.scale = 1 * DESCRIPT_SCALE;
					desNode.opacity = 128;
					desNode.position = this.cmd.rootNode.convertToNodeSpaceAR(this.target.convertToWorldSpaceAR(cc.v2(0, zeroHeight / 2 + 20)));
					tween = cc.tween(desNode).parallel(
						cc.tween(desNode).to(0.2, { opacity: 255 }).to(0.8, { opacity: 0 }),
						cc.tween(desNode).by(0.7, { y: -25 }, { easing: "cubicOut" })
					)
					break;
				}
				case DescribeType.Effect: {
					desNode.scale = 1 * DESCRIPT_SCALE;
					desNode.opacity = 128;
					desNode.position = this.cmd.rootNode.convertToNodeSpaceAR(this.target.convertToWorldSpaceAR(cc.v2(0, zeroHeight / 2)));
					tween = cc.tween(desNode).to(0.1, { scale: 1.5 * DESCRIPT_SCALE, opacity: 255 }).to(0.2, { scale: 1 * DESCRIPT_SCALE }).to(0.8, { opacity: 0 }, { easing: "cubicOut" })
					break;
				}
				default: {
					break;
				}
			}
			if (tween) {
				tween.call(() => {
					this.cmd.assetManager.recyclingDescribe(desNode)
				})
				// this.btlActionMgr.addTweenAction(tween);
				tween.start();
			}
			this._describeDeltas[gruop] = 0;
		})
	}

	/** */
	protected doWaveDescribe(dt: number) {
		if (this.camp != JXBtlCamp.Defenser) return;
		if (this.wheatUp <= 0) return;
		if (this._waveTime < 1) {
			this._waveTime += dt
		} else {
			this._waveTime = 0;
			let text = `wave|Gcrit_+|Gcrit_${this.wheatUp}`;
			this.cmd.addWheat(this.wheatUp);
			let desNode = this.cmd.assetManager.createDescribe(text);
			desNode.color = JXColor.C000000;
			desNode.parent = this.cmd.rootNode;
			desNode.zIndex = Math.max(this.zIndex, JXRB_LAYER_INDEX.DESCRIBE_LAYER);
			let zeroHeight = this.skeleton.node["sHeight"];
			let tween: cc.Tween = null;
			desNode.scale = 1 * DESCRIPT_SCALE;
			desNode.opacity = 128;
			desNode.position = this.cmd.rootNode.convertToNodeSpaceAR(this.target.convertToWorldSpaceAR(cc.v2(this.target.width * 2, zeroHeight + 30)));
			let postion = cc.v2(desNode.position.x, desNode.position.y + 30);
			tween = cc.tween(desNode).to(0.1, { scale: 1.5 * DESCRIPT_SCALE, opacity: 255, position: postion }).to(0.2, { scale: 1 * DESCRIPT_SCALE }).to(0.8, { opacity: 0 }, { easing: "cubicOut" })
			tween.call(() => {
				this.cmd.assetManager.recyclingDescribe(desNode);
			})
			tween.start()
		}
	}

	/**播放受击动画 */
	public showHitAni() {
		this.hitSkeleton.node.active = true;
		this.hitSkeleton.setCompleteListener(() => {
			this.hitSkeleton.node.active = false;
		})
		this.hitSkeleton.setAnimation(0, "animation", false)
	}

	public playSyncAni() {
		this.lvUpSkeleton.node.active = true;
		this.lvUpSkeleton.setCompleteListener(() => {
			this.lvUpSkeleton.node.active = false;
		})
		this.lvUpSkeleton.setAnimation(0, 'heti', false)
	}

	public changeAnimation(actionName: string, loop: boolean = false, completeCb?: any, showEffect: boolean = true): JXCLAction {
		if (!this.skeleton) return;
		let track = this.skeleton.setAnimation(0, actionName, loop);
		if (!completeCb) {
			this.skeleton.setCompleteListener(null)
		} else {
			this.skeleton.setCompleteListener(completeCb);
		}
		if (!!this.effectSkeleton && !!this.effectSkeleton.skeletonData) {
			let aniInfo = this.effectSkeleton.getAnimationInfo(actionName);
			this.effectSkeleton.setCompleteListener(() => {
				this.effectSkeleton.skeletonData = null;
			})
			if (!!aniInfo) {
				this.effectSkeleton.setAnimation(0, actionName, loop);
			} else {
				this.effectSkeleton.setAnimation(0, "effect", loop)
			}
		}
		if (!track) {
			cc.log(`角色标签${this.id},名字${this._name}找不到动作:" + ${actionName}`);
		}
		return { track: track, duration: track.animation.duration };
	}

	// /**更改动画 */
	// public changeAnimation(actionName: string, loop: boolean = false, completeCb?: any, showEffect: boolean = true): JXCLAction {
	// 	if (!this.skeleton) return;
	// 	let track: sp.spine.TrackEntry = this.skeleton.setAnimation(0, actionName, loop);
	// 	let maxDuration: number = track ? track.animation.duration : 0;
	// 	let track2: sp.spine.TrackEntry = null;
	// 	if (!!this.effectSkeleton && !!this.effectSkeleton.skeletonData && showEffect) {
	// 		let aniInfo = this.effectSkeleton.getAnimationInfo(actionName);
	// 		this.effectSkeleton.setCompleteListener(() => {
	// 			this.effectSkeleton.skeletonData = null;
	// 		})
	// 		if (!!aniInfo) {
	// 			track2 = this.effectSkeleton.setAnimation(0, actionName, loop);
	// 		} else {
	// 			track2 = this.effectSkeleton.setAnimation(0, "effect", loop)
	// 		}
	// 	}
	// 	maxDuration = (track2 && track2.animation.duration > maxDuration) ? track2.animation.duration : maxDuration;
	// 	let endTime = GCtrl.now + maxDuration * JXDef.Time.SECOND;

	// 	ViewUtil.taskTick({
	// 		start: GCtrl.now, end: endTime,
	// 		tickTime: 0.1,
	// 		endcb: function () {
	// 			completeCb && completeCb()
	// 		}.bind(this)
	// 	}, this.target)

	// 	if (!track) {
	// 		cc.log(`角色标签${this.id},名字${this._name}找不到动作:" + ${actionName}`);
	// 	}
	// 	return { track: track, duration: maxDuration };
	// }


	/**显示效果动画 */
	public showSingleEffect(actionName: string, loop: boolean = false, completeCb?: any) {
		if (!!this.effectSkeleton && !!this.effectSkeleton.skeletonData) {
			let aniInfo = this.effectSkeleton.getAnimationInfo(actionName);
			let track = null
			if (completeCb) {
				this.effectSkeleton.setCompleteListener(() => {
					this.effectSkeleton.skeletonData = null;
					completeCb()
				})
			} else {
				this.effectSkeleton.setCompleteListener(() => {
					this.effectSkeleton.skeletonData = null;
				})
			}
			if (!!aniInfo) {
				track = this.effectSkeleton.setAnimation(0, actionName, loop);
			} else {
				track = this.effectSkeleton.setAnimation(0, "effect", loop);
			}
			return track ? track.animation.duration : 0
		}
	}


	/**技能动作 */
	public changeSkillAnimation(aniNames: string[], completeCb?: any, showEffect: boolean = true): JXCLAction {
		let duration = 0;
		let index = 0;
		for (let i = 0; i < aniNames.length; i++) {
			let aniInfo = this.skeleton.getAnimationInfo(aniNames[i]);
			if (!aniInfo) {
				cc.log(`角色标签${this.id},名字${this._name}找不到动作:" + ${aniNames[i]}`);
			}
			duration += aniInfo ? aniInfo.duration : 0;
		}
		let nextAction = () => {
			if (index >= aniNames.length) {
				return completeCb && completeCb(true);
			}
			else {
				return this.changeAnimation(aniNames[index++], false, () => nextAction(), showEffect);
			}
		}
		let frist = nextAction();
		return { duration: duration, track: frist.track };
	}

	/**组成对应的动作名 */
	public appendAnimName(base: string, index: number): string {
		return base + '_' + index;
	}

	/**改变为预备动画 */
	public changeParpareAnimation(groupIdx: number, skillId: number) {
		this.addAction(cc.callFunc(() => {
			let state = this.getCurState();
			if (state == this._stopFrameSt.stateID) {
				this.skeleton.paused = false;
			}
			this.changeAnimation(JXEAniNames.qishou, false, () => {
				this._prepareSt.perpareAniName = JXEAniNames.prepare;
				this._prepareSt.skillId = skillId;
				this.changeState(this._prepareSt.stateID);
				this.cmd.evtMgr.post(JXMsg.action.scExit, this);
			})
		}))
	}


	// 骨骼帧事件
	public onSkEventListen(skeleton: sp.Skeleton, track: sp.spine.TrackEntry, event: sp.spine.Event) {
		if (event.data.name === JXEAniEventName.HitAni) {
			// this.showHitAni()
		}
		if (!this._script) {
			return;
		}

		let eventName = event.data.name
		switch (eventName) {
			case JXEAniEventName.CaiAtk: {
				let spinePath = JXSceneEffect["tuteng"];
				let spine = this.cmd.assetManager.createSpine(spinePath);
				let layer = JXRB_LAYER_INDEX.SCENE_EFFECT_LOWER;
				spine.node.anchorY = 0.5;
				spine.node.parent = this.cmd.rootNode;
				spine.node.name = eventName;
				spine.node.position = this.position;
				spine.premultipliedAlpha = false;
				spine.node.zIndex = layer;
				spine.node.scaleX *= (this.dir == JXEDir.Right ? 1 : -1);
				spine.setCompleteListener(() => {
					spine.node.destroy();
					spine = null;
				})
				spine.setAnimation(0, "atk", false);
				break;
			}
			case JXEAniEventName.CaiSkill: {
				let spinePath = JXSceneEffect["tuteng"];
				let spine1: sp.Skeleton = this.cmd.assetManager.createSpine(spinePath);
				let spine2: sp.Skeleton = this.cmd.assetManager.createSpine(spinePath);
				let node1 = this.cmd.rootNode.getChildByName(eventName);
				let node2 = this.cmd.rootNode.getChildByName(eventName + 1);
				node1 && node1.destroy();
				node2 && node2.destroy();;
				spine1.node.active = false;
				spine2.node.active = false;
				let layer = JXRB_LAYER_INDEX.SCENE_EFFECT_LOWER;
				spine1.node.anchorY = 0.5;
				spine1.node.parent = this.cmd.rootNode;
				spine1.node.name = eventName;
				spine1.node.position = this.position;
				spine1.premultipliedAlpha = false;
				spine1.node.zIndex = layer;
				spine1.node.scaleX *= (this.dir == JXEDir.Right ? 1 : -1);
				spine2.node.anchorY = 0.5;
				spine2.node.parent = this.cmd.rootNode;
				spine2.node.name = eventName + 1;
				spine2.node.position = this.position;
				spine2.premultipliedAlpha = false;
				spine2.node.zIndex = layer;
				spine2.node.scaleX *= (this.dir == JXEDir.Right ? 1 : -1);
				spine1.setCompleteListener(() => {
					spine1.setAnimation(0, "zhen", true);
					spine2.node.active = true;
					spine2.setAnimation(0, "guang", true);
					setTimeout(() => {
						if (spine1.node) {
							spine1.node.destroy();
						}
						if (spine2.node) {
							spine2.node.destroy();
						}
						spine2 = null;
						spine1 = null;
					}, 2000)
				})
				spine1.node.active = true;
				spine1.setAnimation(0, "skill", false);
				break;
			}
			case JXEAniEventName.BlackStart: {
				let node = this.cmd.rootNode.getChildByName(SCENE_EFFECT_NODE_NAME.BLACK);
				if (!node) {
					node = new cc.Node(SCENE_EFFECT_NODE_NAME.BLACK);
					node.color = cc.Color.BLACK;
					node.width = GCtrl.actualSize.width;
					node.height = GCtrl.actualSize.height;
					let sp = node.addComponent(cc.Sprite);
					sp.sizeMode = cc.Sprite.SizeMode.CUSTOM;
					this.cmd.assetManager.assetImpl.spriteFrame(sp, Res.texture.single);
					node.parent = this.cmd.rootNode;
					node.zIndex = JXRB_LAYER_INDEX.BLACK;
				}

				let targets = this._script.targets;
				for (let i = 0; i < targets.length; i++) {
					targets[i].zIndex = JXRB_LAYER_INDEX.ROLE_SPECIAL + targets[i].zIndex;
				}
				this._script.execer.zIndex += JXRB_LAYER_INDEX.ROLE_SPECIAL;

				node.opacity = 0;
				let tween = cc.tween(node).to(0.1, { opacity: 255 * 0.9 });
				this.btlActionMgr.addTweenAction(tween);

				break;
			}
			case JXEAniEventName.BlackEnd: {
				let node = this.cmd.rootNode.getChildByName(SCENE_EFFECT_NODE_NAME.BLACK);
				if (!node) break;

				let targets = this._script.targets;
				for (let i = 0; i < targets.length; i++) {
					targets[i].zIndex = -targets[i].y;
				}

				this._script.execer.zIndex -= JXRB_LAYER_INDEX.ROLE_SPECIAL;
				let tween = cc.tween(node).to(0.1, { opacity: 0 }).removeSelf();
				this.btlActionMgr.addTweenAction(tween);
				break;
			}
			default: {
				let isBullet = this._script.isBullet(this._script.actionRaw.bullet);
				if (eventName == JXEAniEventName.Shoot) {
					if (isBullet) {
						!this._script.isShoots && this._script.shoots();
						return;
					}

					cc.log(`角色标签${this.id},名字${this._name} 动作${track.animation.name}` + ", 配置上这不是一个子弹，但是存在子弹事件'");
					return;
				}
				else {
					if (isBullet) {
						if (!this._script.isShoots) {
							cc.log(`角色标签${this.id},名字${this._name} 动作${track.animation.name}` + "这是一个子弹" + this._script.skillId());
						}
						return;
					}
					// 非子弹事件走向
					this._script.onAnimationCollision(track, eventName);
				}
			}
		}
	}

	/** 添加帧动画 */
	public createEffect(aniConfig: EffectConfigure, cb?): cc.Animation {
		let animation = this.cmd.assetManager.createEffect(aniConfig.effect, aniConfig.root, aniConfig.loop ? cc.WrapMode.Loop : cc.WrapMode.Normal, () => {
			animation.node.destroy();
			if (cb) cb();
		})

		if (!this.target) {
			return;
		}

		let layer = this.target.getChildByName(JXRB_ROLE_LAYER_NAME.EFFECT);
		animation.node.parent = layer;

		if (aniConfig.anchor) {
			animation.node.anchorX = aniConfig.anchor.x;
			animation.node.anchorY = aniConfig.anchor.y;
		}
		if (aniConfig.anchOffset) {
			animation.node.x = this.skeleton.node["sWidth"] * aniConfig.anchOffset.x;
			animation.node.y = this.skeleton.node["sHeight"] * aniConfig.anchOffset.y;
		}
		if (aniConfig.offset) {
			animation.node.x += aniConfig.offset.x;
			animation.node.y += aniConfig.offset.y;
		}

		animation.node.scaleX *= this.dir;
		animation.play(COMMON_ANI_NAME);
		return animation;
	}



	/******************************************************************************脚本控制所需********************************************************************************************** */
	/**脚本执行 */
	public execScript(script: JXRBScript) {
		let self = this;
		this.addAction(cc.callFunc(() => {
			if (self._script) {
				cc.error('the script by ID = ' + self._script.ID + 'is running!');
				if (self._script._bullets && self._script._bullets.length && !self._script.isShoots) {
					self._script._endFlag = 0;
					self._script.shoots();
				} else {
					self._script.exit();
					self._script = null;
				}
				return;
			}
			self._script = script;

			// 提前到目标的最上层
			if (script.targets.length > 0) {
				let maxIndex = null;
				for (let i = 0, targets = script.targets; i < targets.length; i++) {
					if (!maxIndex) {
						maxIndex = targets[i].zIndex;
					} else {
						maxIndex = Math.max(maxIndex, targets[i].zIndex);
					}
				}
				this.zIndex = Math.max(this.zIndex, maxIndex) + 1;
			} else {
				this.zIndex = JXRB_LAYER_INDEX.ROLE_SPECIAL + this.zIndex;
			}

			let skillId = script.skillId();
			if (skillId && this._prepareSt && this._prepareSt.skillId) {
				if (this._prepareSt.skillId == skillId) {
					this._prepareSt.perpareAniName = null;
					this._prepareSt.skillId = null;
				}
			}
			this.changeState(JXEState.Skill);
			script.exec();
		}))
	}

	/** 
       * 脚本执行完成
       * @param unRestPos 不需要回到起始位置
       */
	public scrpitExit(script: JXRBScript, resetPos: boolean = true) {
		if (script != this._script) {
			cc.error('the exit scrpit is not equal mine script! please check the logic!');
			return;
		}

		let skillAns = script._aniConfig.ansInfo as JXAnsSkillInfo;
		if (script.existAcions && script.existAcions.length > 0) {
			for (let i = 0; i < script.existAcions.length; i++) {
				this.addAction(script.existAcions.shift(), true);
			}
			script.existAcions = [];
		}
		if (skillAns.model && skillAns.model == JXBtlAns_PressModel.PrepareEnd) {
			this.changeState(JXEState.Idle);
		}
		else {
			this.changePreState()
		}

		if (resetPos) {
			// this.setDir(this.iKey < ICampKeyCode ? JXEDir.Right : JXEDir.Left);
			if (!this.position.equals(this.birthPos)) {
				this.position = this.birthPos;
			}
		}
		this.zIndex = -this.y;
		this.cmd.evtMgr.post(JXMsg.action.scExit, this);
		this._script = null;
		if (this.effectSkeleton && this.effectSkeleton.skeletonData) {
			this.effectSkeleton.skeletonData = null;
		}
	}

	/**
	 * 脚本执行间断
	 * @param script 
	 */
	public scrpitBreak(script: JXRBScript) {
		this._script = null;
		if (this.getPreState() != JXEState.Idle) {
			this.changeState(JXEState.Idle);
		}
		this.zIndex = -this.y;
	}


	/************************************************************************************动作管理********************************************************************************************/

	public addActionToMgr(impl: CCNodeImpl, action: cc.Action, pause: boolean = false): cc.Action {
		this.btlActionMgr.addAction(action, impl, pause);
		return action;
	}

	/**添加动作 */
	public addAction(action: cc.Action, isQueue = true) {
		if (isQueue) {
			action.setTag(QUEUE_ACTION_TAG);
			this._actionList.push(action);
		}
		else {
			this.addActionToMgr(this, action);
		}
	}

	/**顶部添加动作 */
	public addTopAction(action: cc.Action) {
		action.setTag(QUEUE_ACTION_TAG);
		this._actionList.push(action);
	}

	/**顶部移除 */
	public removeTopQueueAction() {
		this.btlActionMgr.removeAction(this._actionList[0], true);
	}
}